package org.coursera.challenge;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashSet;
import java.util.List;
import java.util.Set;


public class Exam {

	public static void main(String[] args) {
		Exam exam = new Exam();
		// 1. System.out.println(exam.patternCount("CGGAGAGCCACTCGGGCACGGAGAGTCCGGAGAGCGGAGAGCGGAGAGGCGGAGAGAGCACCGGAGAGCCAGGACGGAGAGCGGAGAGCTAGTCGGAGAGTTCGGAGAGGATCGGAGAGGCGGAGAGCAAGCCACCTTAGCGGAGAGACGGAGAGAGGCCTTGGCCGCTCCGGAGAGCGGAGAGGGCGGAGAGGCGGAGAGGTACGGAGAGACCGGAGAGCGGAGAGTTAATCAGAACGTCGGAGAGAACGGAGAGCGGAGAGTGCGGAGAGACGGAGAGTTCGGAGAGCAGGAACACGGAGAGGGGATATTTTCCGGAGAGAGCCATACGGAGAGGCGGAGAGCCGGAGAGAAACTCGGAGAGTGTCGGAGAGCACGGCTTGCGGAGAGACCGCGGAGAGCGGAGAGGTTGGCGGAGAGATCCCGGAGAGCGGAGAGCCGGAGAGGCGGAGAGTGTTGAGTTCGGAGAGCCGGAGAGATACGGAGAGCGGAGAGTGCGGAGAGCCAGTGATACTCGCGGAGAGCGGAGAGTGCCTGGAAGCGCGGAGAGGTAGCGGAGAGGCCCGGAGAGCTACATGCGGAGAGAGTGCGCGGAGAGCGGAGAGTCCCCGGAGAGTCCGGAGAGCGGAGAGCGGAGAGTCGGACGGAGAGCCCCCGGAGAGCGGAGAGAGTCACGGAGAGCGGAGAGCGGAGAGGCGGAGAGTCGGAGAGTTGCGGAGAGACCGGAGAGGGGGCTGGACTAGTCGCACGGAGAGTGGACGGAGAGCCCTCCCGGAGAGCGGAGAGCGAGTCGGAGAGCCGGAGAGCCTTCGGAGAGCGGGCACATCGGAGAGCACTCAGCGGAGAGCGGAGAGCGGAGAGCGGAGAGTCGGAGAGCTACCGGAGAGGCATCCGGAGAGTCGGAGAGCCGGAGAGCAGCGCGGAGAGCGGAGAGGCGGAGAGCGCGGAGAGCGGAGAGACGGAGAGCGGAGAGTTACCGGAGAGAGGTTTCGGAGAGCGGAGAGCGGAGAGCCGGAGAGGAACACC", "CGGAGAGCG"));
		// 2. System.out.println(exam.frequentWords("GTGGAACCCCACTCAGTATATTCGGTGGAACCGCTAACTACCACTCAGGTGGAACCTATATTCGCCACTCAGCCACTCAGGTGGAACCGTGGAACCCCACTCAGGTGGAACCGTGGAACCCCACTCAGTATATTCGGTGGAACCTATATTCGTATATTCGTATATTCGCTTAGTAAGCTAACTATATATTCGTATATTCGTATATTCGCTTAGTAAGTGGAACCCCACTCAGCCACTCAGTATATTCGTATATTCGCCACTCAGGCTAACTACCACTCAGCTTAGTAACCACTCAGTATATTCGGTGGAACCCTTAGTAAGTGGAACCGCTAACTATATATTCGCCACTCAGGCTAACTATATATTCGTATATTCGCTTAGTAACTTAGTAACCACTCAGCTTAGTAACCACTCAGCCACTCAGGTGGAACCTATATTCGTATATTCGGTGGAACCCCACTCAGCTTAGTAACTTAGTAATATATTCGTATATTCGGTGGAACCGCTAACTATATATTCGCCACTCAGCCACTCAGTATATTCGGTGGAACCCCACTCAGGTGGAACCGCTAACTATATATTCGTATATTCGTATATTCGCTTAGTAACTTAGTAATATATTCGGCTAACTATATATTCGCCACTCAGCCACTCAGGTGGAACCCCACTCAGGTGGAACCGTGGAACCGTGGAACCCCACTCAGGTGGAACCGTGGAACCTATATTCGGTGGAACCTATATTCGGCTAACTACTTAGTAAGTGGAACCTATATTCGCCACTCAGCCACTCAGGTGGAACCGTGGAACCCCACTCAGCCACTCAGGCTAACTAGTGGAACCGCTAACTAGTGGAACCGCTAACTACTTAGTAACTTAGTAAGTGGAACCCCACTCAGCCACTCAGCCACTCAGTATATTCGCCACTCAGCCACTCAGTATATTCG", 11));
		// 3. exam.reverseDNAComplementFile("datasource/challenge-003-reversedna.txt");
		// 4. exam.patternLocation("datasource/challenge-004-patternloc.txt", "AGACTTGAG");
		 exam.findingClumps("datasource/test/challenge-005-test1.txt", 5, 50, 4);
		// 5. exam.findingClumps("datasource/challenge-005-findclumps.txt", 11, 485, 17);
		// 5. exam.findingClumps("datasource/Vibrio_cholerae.txt", 9, 500, 3);
	}

	/**
	 * Return the number of pattern in the given genome 
	 * 
	 * @param data
	 * @param pattern
	 * @return
	 */
	private int patternCount(String data, String pattern) {
		int result = 0;
		if (data!=null && pattern!=null) {
			int patternLength = pattern.length();
			for (int i=0; i<=data.length()-patternLength; i++) {
				if (pattern.equals(data.substring(i, i+patternLength))) 
						result++;
			}			
		}
		
		return result;
	}

	private int patternCount(StringBuffer data, String pattern) {
		int result = 0;
		if (data!=null && pattern!=null) {
			int patternLength = pattern.length();
			for (int i=0; i<=data.length()-patternLength; i++) {
				if (pattern.equals(data.substring(i, i+patternLength))) 
						result++;
			}			
		}
		
		return result;
	}

	
	/** Find the most frequent patterns in the given genome, may have more than one
	 * 
	 * @param data
	 * @param k - pattern length
	 * @return
	 */
	private Set<String> frequentWords(String data, int k) {
		Set<String> result = new HashSet<String>();
		List<Integer> count = new ArrayList<Integer>();
		
		if (data!=null && k!=0) {
			int maxCount = 0;
			for (int i=0; i<=data.length()-k; i++) {
				String pattern = data.substring(i, i+k);
				int pCount = patternCount(data, pattern);
				count.add(pCount);
				if (pCount>maxCount)
					maxCount = pCount;
			}

			for (int i=0; i<=data.length()-k; i++) {
				if (count.get(i) == maxCount)
					// if duplicate pattern, Set will not add it
					result.add(data.substring(i, i+k)); 
			}
		}
		
		return result;		
	}

	/**
	 * DNA complement, the reverse start from the back
	 * 
	 * @param data
	 * @return
	 */
	private String reverseDNAComplement(String data) {
		String result = null;
		
		if (data!=null) {
			StringBuffer sb = new StringBuffer();
			for (int i=data.length()-1; i>=0; i--) {
				switch ((data.charAt(i))) {
				case 'A' : sb.append('T'); break;
				case 'T' : sb.append('A'); break;
				case 'C' : sb.append('G'); break;
				case 'G' : sb.append('C'); break;
				}
			}
			result = sb.toString();
		}
		
		return result;
	}

	/**
	 * DNA complement, the reverse start from the back
	 * 
	 * @param data
	 * @return file output
	 */
	private void reverseDNAComplementFile(String fileName) {
		InputStream is = null;
		BufferedWriter out = null;
		try {
			if (fileName!=null) {
				
				is = new FileInputStream(new File(fileName));
				Reader reader = new InputStreamReader(is, Charset.defaultCharset());
				Reader buffer = new BufferedReader(reader);
				
				int c=0;
				StringBuffer sb = new StringBuffer();
				while ((c=buffer.read())!=-1) {
					char ch = (char) c;
					switch (ch) {
						case 'A' : sb.append('T'); break;
						case 'T' : sb.append('A'); break;
						case 'C' : sb.append('G'); break;
						case 'G' : sb.append('C'); break;
					}
				}

			   out  = new BufferedWriter(new FileWriter("datasource/out.txt"));  
		       out.write(sb.reverse().toString());  
		       out.flush();  
		       System.out.println("Successful.");
			}
		} catch (IOException e) {
			e.printStackTrace();
		}		
		
		finally {
			try {
				if (is!=null)
					is.close();
				if (out!=null)
					out.close();				
			} catch(IOException e) {
				e.printStackTrace();
			}
		}
	}

	
	/**
	 * Pattern Matching Problem
	 * Given gnome and pattern, return all the position.
	 * 
	 * @param fileName
	 */
	private void patternLocation(String fileName, String pattern) {
		InputStream is = null;
		BufferedWriter out = null;
		try {
			if (fileName!=null) {
				
				is = new FileInputStream(new File(fileName));
				Reader reader = new InputStreamReader(is, Charset.defaultCharset());
				Reader buffer = new BufferedReader(reader);
				
				// read all and put it in the buffer
				StringBuffer sb = new StringBuffer();
				int c=0;
				while ((c=buffer.read())!=-1) {
					sb.append((char) c);
				}

				int patternLength = pattern.length();
				for (int i=0; i<=sb.length()-patternLength; i++) {
					if (pattern.equals(sb.substring(i, i+patternLength))) 
						System.out.print(i+" ");
				}			
				//System.out.println("Successful.");
			}
		} catch (IOException e) {
			e.printStackTrace();
		}		
		
		finally {
			try {
				if (is!=null)
					is.close();
				if (out!=null)
					out.close();				
			} catch(IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * The frequency array. Build array of 4power-k, remember possible character is ATCG (how about uracil?)
	 * According to the dictionary, pattern mapped to array index, and the frequency
	 * need function getPattern(given index) and get the frequency (given pattern, k)	
	 */

	/**
	 * Return patterns forming clumps in the given gnome
	 * 
	 * @param fileName 
	 * @param kMers - k-mers
	 * @param windowLength - window to be computed one at a time
	 * @param minThreshold - minimum threshold for occurence of  k-mers to be considered as clumps in a given window
	 */
	public void findingClumps(String fileName, int kMers, int windowLength, int minThreshold) {
		InputStream is = null;
		Reader buffer = null;
		try {
			if (fileName!=null) {
				
				is 					= new FileInputStream(new File(fileName));
				Reader reader 		= new InputStreamReader(is, Charset.defaultCharset());								
				buffer 				= new BufferedReader(reader);

				// int patternLength 	= pattern.length();
				StringBuffer sb		= new StringBuffer();
				char[] genomeSlice 	= new char[windowLength];
				
				System.out.println(Calendar.getInstance().getTime());
				// Initialization
				if (buffer.read(genomeSlice,0,windowLength)==windowLength) {
					sb.append(genomeSlice);
					System.out.println(sb);
					Set<String> result = frequentWords(sb, kMers, minThreshold);
					System.out.println(result);

					//System.out.println(System.currentTimeMillis());

					int ch =0;
					while ((ch = buffer.read()) != -1) {
						// shift window
						sb.append((char)ch);
						sb.deleteCharAt(0);
						//System.out.println(sb);
						Set result2 = frequentWords(sb, kMers, minThreshold);

						//System.out.println(result2);
						if (!result2.isEmpty())
							result.addAll(result2);
						
					}
					System.out.println(Calendar.getInstance().getTime());
					System.out.println("Result: " + result);
					
				}

				System.out.println("Done. Remember to replace comma with space");
			}
		} catch (IOException e) {
			e.printStackTrace();
		}		
		
		finally {
			try {
				if (is!=null)
					is.close();
				if (buffer!=null)
					buffer.close();				
			} catch(IOException e) {
				e.printStackTrace();
			}
		}			
	}

	/**
	 * Frequent pattern more or equal than t for k-mers
	 * 
	 * @param data
	 * @param k - k-mers
	 * @param t - more or equal
	 * @return
	 */
	private Set<String> frequentWords(StringBuffer data, int k, int t) {
		Set<String> result = new HashSet<String>();
		List<Integer> count = new ArrayList<Integer>();
		
		if (data!=null && k!=0) {
			for (int i=0; i<=data.length()-k; i++) {
				String pattern = data.substring(i, i+k);
				int pCount = patternCount(data, pattern);
				count.add(pCount);
			}

			for (int i=0; i<=data.length()-k; i++) {
				if (count.get(i) >= t)
					// if duplicate pattern, Set will not add it
					result.add(data.substring(i, i+k)); 
			}
		}
		
		return result;		
	}


}
